package com.fsoft.core.utils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.channels.FileChannel;
import java.util.Enumeration;
import java.util.zip.ZipInputStream;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.web.subject.WebSubject;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fsoft.core.shiro.ShiroContext;

/*********************************
 * F-Soft 文件夹管理工具类
 * @package com.fsoft.core.utils
 * @author Fish
 * @email it.fish2010@foxmail.com
 * @date 2020-02-29
 * @CopyRight © F-Soft
 **********************************/
public class FileManage {
	private static File appdataDir;
	private static File workSpace;
	private static File uploadSpace;
	private static File expSpace;
	private static File impSpace;
	private static File tempSpace;

	public static final String f_zip = ".zip";
	public static final String f_xml = ".xml";
	public static final String f_csv = ".csv";
	public static final String f_txt = ".txt";
	public static final String f_doc = ".doc";
	public static final String f_xls = ".xls";
	public static final String f_pdf = ".pdf";

	private static Logger log = LoggerFactory.getLogger(FileManage.class);
	/***
	 * 附件压缩包内后缀名格式
	 */
	public static final String f_shzl = ".shzl";
	public static final int BUFFER_SIZE = 1024 * 1024 * 2;

	/***
	 * F-Soft 本地文件的绝对地址
	 * @author it_software(it.fish2010@foxmail.com)
	 * @date 2020-02-29
	 * @param localFullPath
	 * @return
	 */
	public static final String formatWebPath(String localFullPath) {
		int idx = localFullPath.indexOf("static");
		String webPath = "";
		if (idx > 0) {
			webPath = localFullPath.substring(idx);
		}
		// webPath 的路径替换
		return webPath.replace("\\", "/").replace("\\\\", "//");
	}

	/***
	 * 获取附件目录
	 * 
	 * @author licheng
	 * @date 2018年5月14日
	 * @return
	 */
	public static final File getAppdataDir() {
		if (appdataDir == null) {
			String path = "/WEB-INF/static";
			try {
				// 通过shiro获取上下文，获取附件目录
				ServletRequest request = ((WebSubject) ShiroContext.getSubject()).getServletRequest();
				path = ((HttpServletRequest) request).getSession().getServletContext().getRealPath(path);
				appdataDir = mkdir(path);
			} catch (Exception e) {
				log.error("无法获取appdataDir绝对路径，" + e.getMessage());
				appdataDir = new File(path);
			}
		}
		return appdataDir;

	}

	/**
	 * F-Soft 兼容Controller获取绝对路径问题
	 * @author it_software(it.fish2010@foxmail.com)
	 * @date 2020-02-29
	 * @param request
	 * @return
	 */
	public static final File getAppdataDir(HttpServletRequest request) {
		String path = "";
		try {
			path = request.getSession().getServletContext().getRealPath("/WEB-INF/static");
			appdataDir = mkdir(path);
		} catch (Exception e) {
			log.error("无法通过HttpServletRequest获取appdataDir绝对路径" + e.getMessage());
			appdataDir = getAppdataDir();
		}
		return appdataDir;
	}

	/***
	 * 文件处理主目录
	 * 
	 * @return
	 */
	public static final File getWorkSpace() {
		if (workSpace == null) {
			workSpace = createSpace(getAppdataDir(), "workSpace");
		}
		return workSpace;
	}

	/***
	 * 文件导出目录
	 * 
	 * @return
	 */
	public static File getExpSpace() {
		if (expSpace == null || !expSpace.exists())
			expSpace = createSpace(getWorkSpace(), "expSpace");
		return expSpace;
	}

	/***
	 * 数据文件导入目录
	 * 
	 * @return
	 */
	public static File getImpSpace() {
		if (impSpace == null || !impSpace.exists())
			impSpace = createSpace(getWorkSpace(), "impSpace");
		return impSpace;
	}

	/****
	 * 临时文件夹
	 * 
	 * @return
	 */
	public static File getTempSpace() {
		if (tempSpace == null || !tempSpace.exists())
			tempSpace = createSpace(getWorkSpace(), "tempSpace");
		return tempSpace;
	}

	/***
	 * 获取文件上传目录
	 * 
	 * @user licheng
	 * @date 2017年12月21日
	 * @return
	 */
	public static File getUploadSpace() {
		if (uploadSpace == null || !uploadSpace.exists())
			uploadSpace = createSpace(getWorkSpace(), "uploadSpace");
		return uploadSpace;
	}

	/***
	 * @author licheng
	 * @date 2018年5月15日
	 * @param workSpace
	 * @param spaceName
	 * @return
	 */
	public static File getSpace(File workSpace, String spaceName) {
		File space = mkdir(workSpace, spaceName, false);
		if (space == null || !space.exists())
			return null;
		return space;
	}

	/**
	 * 在临时文件夹下生成一个随机目录
	 * 
	 * @author licheng
	 * @date 2018年2月24日
	 * @return
	 */
	public static final File randomTempSpace() {
		return randomSpace(getTempSpace());
	}

	/***
	 * 在指定的目录下生成一个随机文件夹
	 * 
	 * @author licheng
	 * @date 2018年2月24日
	 * @param workSpace
	 * @return
	 */
	public static File randomSpace(File workSpace) {
		String spaceName = OgnlUtils.randomLowerCaseId();
		return mkdir(workSpace, spaceName, true);
	}

	public static final File createSpace(File parent, String name) {
		return mkdir(parent, name, true);
	}

	/***
	 * 在指定的目录下生成文件夹,根据mkdir属性判断，是否需要强行创建文件夹
	 * 
	 * @author licheng
	 * @date 2018年5月15日
	 * @param parent
	 * @param name
	 * @param mkdir
	 * @return
	 */
	public static final File mkdir(File parent, String name, boolean mkdir) {
		File dir = new File(parent, name);
		if (!dir.exists() && mkdir)
			dir.mkdirs();
		return dir;
	}

	/***
	 * 获取一个目录
	 * 
	 * @param path
	 * @return
	 */
	public static final File mkdir(String path) {
		File f = new File(path);
		if (!f.exists())
			f.mkdirs();
		return f;
	}

	/****
	 * 在指定的目录下创建文件。如果文件已经存在，则删除后重新创建
	 * 
	 * @param workSpace
	 * @param fileName
	 * @return
	 */
	public static File createFile(File workSpace, String fileName) {
		return getFile(workSpace, fileName, true);
	}

	/****
	 * 文件复制，复制成功，源文件srcFile还保留在原来的目录中
	 * 
	 * @param srcFile 源文件
	 * @param tarFile 目标文件
	 * @return
	 */
	public static void copy(File srcFile, File tarFile) {
		int length = 2097152;
		FileInputStream in = null;
		FileOutputStream out = null;
		try {
			in = new FileInputStream(srcFile);
			out = new FileOutputStream(tarFile);
			FileChannel inC = in.getChannel();
			FileChannel outC = out.getChannel();
			while (true) {
				if (inC.position() == inC.size()) {
					inC.close();
					outC.close();
					break;
				}
				if ((inC.size() - inC.position()) < 20971520)
					length = (int) (inC.size() - inC.position());
				else
					length = 20971520;
				inC.transferTo(inC.position(), length, outC);
				inC.position(inC.position() + length);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			close(in);
			close(out);
		}
	}

	/***
	 * 文件移动。移动成功后，源文件删除，只保留目标文件
	 * 
	 * @param srcFile
	 * @param tarFile
	 */
	public static void move(File srcFile, File tarFile) {
		copy(srcFile, tarFile);
		delete(srcFile);
	}

	public static File getFile(String filePath) {
		File f = new File(filePath);
		if (f.exists() && f.isFile())
			return f;
		return null;
	}

	/***
	 * 获取指定目录下的文件
	 * 
	 * @author licheng
	 * @date 2018年6月26日
	 * @param work 文件目录
	 * @param filename 文件名
	 * @return
	 */
	public static File getFile(File work, String name) {
		return getFile(work, name, false);
	}

	/***
	 * 在指定的目录下创建文件.如果文件已经存在，则由over属性控制是否需要强行覆盖。
	 * 
	 * @param work 文件目录
	 * @param filename 文件名
	 * @param over 是否强行覆盖
	 * @return
	 */
	public static File getFile(File work, String filename, boolean over) {
		File file = new File(work, filename);
		try {
			if (file.exists() && over) {
				delete(file);
				file.createNewFile();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return file;
	}

	/***
	 * 文件过滤器:返回文件夹中的文件
	 * 
	 * @author licheng
	 * @date 2018年5月4日
	 * @param workSpace 文件夹
	 * @param isFile 是否只返回文件
	 * @return
	 */
	public static File[] list(File workSpace, boolean isFile) {
		if (!isFile)
			return workSpace.listFiles();
		return workSpace.listFiles(new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				if (pathname.isFile())
					return true;
				return false;
			}
		});
	}

	/***
	 * 文件过滤器:返回文件夹中的文件
	 * 
	 * @param workSpace
	 * @return
	 */
	public static File[] list(File workSpace, final String fileType) {
		return workSpace.listFiles(new FileFilter() {
			@Override
			public boolean accept(File f) {
				String name = f.getName().toUpperCase();
				if (f.isFile() && name.endsWith(fileType.toUpperCase()))
					return true;
				return false;
			}
		});
	}

	/***
	 * 排除过滤： 扫描指定文件夹内的文件，过滤指定类型的文件
	 * 
	 * @param workSpace
	 * @return
	 */
	public static File[] listNotIn(File workSpace, final String fileType) {
		return workSpace.listFiles(new FileFilter() {
			@Override
			public boolean accept(File f) {
				String name = f.getName().toUpperCase();
				if (name.endsWith(fileType.toUpperCase()))
					return false;
				return true;
			}
		});
	}

	/****
	 * 压缩文件：把多个文件压缩成为一个文件<BR>
	 * 如果源文件列表(files)包含文件夹，文件夹会被跳过
	 * 
	 * @user licheng
	 * @date 2017年12月21日
	 * @param zipFile 目标文件（压缩后的文件）
	 * @param files 源文件（被压缩的文件）
	 */
	public static final void compress(File zipFile, File[] files) {
		ZipOutputStream zos = null;
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(zipFile);
			zos = new ZipOutputStream(fos);
			zos.setMethod(ZipOutputStream.DEFLATED);
			zos.setEncoding("UTF-8");
			for (File f_file : files) {
				FileInputStream fis = null;
				try {
					if (f_file.isDirectory())
						continue;
					fis = new FileInputStream(f_file);
					zos.putNextEntry(new ZipEntry(f_file.getName()));
					int len = 0;
					byte[] b = new byte[BUFFER_SIZE];
					while ((len = fis.read(b)) != -1) {
						zos.write(b, 0, len);
					}
				} finally {
					close(fis);
					fis = null;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			close(zos);
			close(fos);
		}
	}

	/**
	 * 文件解压： 把文件加压缩到指定的目录下
	 * 
	 * @user licheng
	 * @date 2017年12月21日
	 * @param zipFile 源文件
	 * @param workSpace 文件解压后存放目录
	 * @throws Exception
	 */
	public static final void decompress(File file, File workSpace) throws Exception {
		if (file == null || !file.isFile())
			throw new RuntimeException("无效的压缩文件");
		if (!file.exists())
			throw new RuntimeException("文件不存在");
		if (workSpace == null || workSpace.isFile())
			throw new RuntimeException("无效的解压目录");
		if (!workSpace.exists())
			throw new RuntimeException("解压目录不存在");
		ZipFile f_zip = null;
		try {
			f_zip = new ZipFile(file, "GBK");
		} catch (Exception e) {
			throw new Exception("不是有效的压缩文件,请检查文件属性或重新上传");
		}
		Enumeration<ZipEntry> files = f_zip.getEntries();
		while (files.hasMoreElements()) {
			ZipEntry entry = files.nextElement();
			// 实例化解压后文件对象
			File outFile = createFile(workSpace, entry.getName());
			BufferedInputStream bufferedInputStream = null;
			BufferedOutputStream bufferedOutputStream = null;
			try {
				// 获取读取条目的输入流
				bufferedInputStream = new BufferedInputStream(f_zip.getInputStream(entry));
				// 获取解压后文件的输出流
				bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(outFile));
				// 读取条目，并写入解压后文件
				byte[] buffer = new byte[BUFFER_SIZE];
				int readCount = -1;
				while ((readCount = bufferedInputStream.read(buffer)) != -1) {
					bufferedOutputStream.write(buffer, 0, readCount);
				}
				bufferedOutputStream.flush();
			} catch (Exception ex) {

			} finally {
				close(bufferedOutputStream);
				close(bufferedInputStream);
			}
		}
		close(f_zip);
	}

	private static void close(ZipFile zip) throws IOException {
		if (zip != null)
			zip.close();
	}

	public static void close(BufferedOutputStream bos) {
		try {
			if (bos != null)
				bos.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void close(BufferedInputStream bis) {
		try {
			if (bis != null)
				bis.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * 关闭压缩文件流
	 * 
	 * @user licheng
	 * @date 2017年12月28日
	 * @param zis
	 */
	public static void close(ZipInputStream zis) {
		try {
			if (zis != null)
				zis.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static void close(InputStream is) {
		try {
			if (is != null)
				is.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * 关闭文件输入流
	 * 
	 * @user licheng
	 * @date 2017年12月21日
	 * @param inputStream 输入流
	 */
	public static void close(FileInputStream inputStream) {
		try {
			if (inputStream != null)
				inputStream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void close(InputStreamReader isReader) {
		try {
			if (isReader != null)
				isReader.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/***
	 * 关闭文件输出流
	 * 
	 * @user licheng
	 * @date 2017年12月21日
	 * @param fos
	 */
	public static void close(OutputStream fos) {
		try {
			if (fos != null) {
				fos.flush();
				fos.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/***
	 * 关闭压缩文件输出流
	 * 
	 * @user licheng
	 * @date 2017年12月21日
	 * @param zos
	 */
	private static void close(ZipOutputStream zos) {
		try {
			if (zos != null) {
				zos.flush();
				zos.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/***
	 * 删除文件，尝试删除10次
	 * 
	 * @param file
	 */
	private static void delete(File file) {
		int k = 1;
		while (true && k <= 10) {
			try {
				file.delete();
			} catch (Exception e) {
				e.printStackTrace();
			}
			k++;
		}
	}

	/***
	 * 递归删除一个目录
	 * 
	 * @param file
	 */
	public static void drop(File file) {
		if (file == null)
			return;
		try {
			if (file.isFile() || file.listFiles().length == 0)
				delete(file);
			else {
				File[] fs = file.listFiles();
				for (File f : fs)
					drop(f);
				delete(file);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void close(OutputStreamWriter out) {
		if (out != null) {
			try {
				out.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

}
